/************************************************************************************
   Copyright (C) 2020 MariaDB Corporation AB

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not see <http://www.gnu.org/licenses>
   or write to the Free Software Foundation, Inc.,
   51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/


#ifndef _BASEPREPARESTATEMENT_H_
#define _BASEPREPARESTATEMENT_H_

#include "Consts.h"

#include "PreparedStatement.hpp"

#include "ColumnType.h"
#include "parameters/ParameterHolder.h"

#include "MariaDbStatement.h"

namespace sql
{
namespace mariadb
{
class BasePrepareStatement : public PreparedStatement
{
public:
   /*static const DateTimeFormatter SPEC_ISO_ZONED_DATE_TIME;*/ /*new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.optionalStart()
.appendLiteral('T')
.optionalEnd()
.optionalStart()
.appendLiteral(' ')
.optionalEnd()
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendOffsetId()
.optionalStart()
.appendLiteral('[')
.parseCaseSensitive()
.appendZoneRegionId()
.appendLiteral(']')
.toFormatter()*/

protected:
  Unique::MariaDbStatement stmt;
  MariaDbConnection* connection;

  int32_t autoGeneratedKeys;
  bool hasLongData =false;
//private:
  bool useFractionalSeconds;
  bool noBackslashEscapes;
  /** Pointers to the factory and protocol owned by stmt(shared ownship)
      If connection object gets deleted - stmt will still have valid Protocol object.
      That helps not to crash and throw exception
  */
  Shared::ExceptionFactory exceptionFactory;
  Protocol* protocol;

//public:
  BasePrepareStatement(
    MariaDbConnection* connection,
    int32_t resultSetScrollType,
    int32_t resultSetConcurrency,
    int32_t autoGeneratedKeys,
    Shared::ExceptionFactory& factory);

public:
  virtual ~BasePrepareStatement(){}

protected:
  virtual bool executeInternal(int32_t fetchSize)=0;
public:
  operator MariaDbStatement* () { return stmt.get(); }
  /**
  * Retrieves the number, types and properties of this <code>PreparedStatement</code> object's
  * parameters.
  *
  * @return a <code>ParameterMetaData</code> object that contains information about the number,
  *     types and properties for each parameter marker of this <code>PreparedStatement</code>
  *     object
  * @throws SQLException if a database access error occurs or this method is called on a closed
  *     <code>PreparedStatement</code>
  * @see ParameterMetaData
  */
  virtual ParameterMetaData* getParameterMetaData()=0;
  virtual void setParameter(int32_t parameterIndex, ParameterHolder* holder)=0;

#ifdef MAYBE_IN_NEXTVERSION
  void setBlob(int32_t parameterIndex, Blob* blob);
  void setClob(int32_t parameterIndex, Clob* clob);
  void setClob(int32_t parameterIndex, std::istringstream* reader,const int64_t length);
  void setClob(int32_t parameterIndex, std::istringstream* reader);
  void setCharacterStream(int32_t parameterIndex, std::istringstream* reader, int32_t length);
  void setCharacterStream(int32_t parameterIndex, std::istringstream* reader, int64_t length);
  void setCharacterStream(int32_t parameterIndex, std::istringstream* reader);
  void setNString(int32_t parameterIndex,const SQLString& value);
  void setNCharacterStream(int32_t parameterIndex,const std::istringstream& value,const int64_t length);
  void setNCharacterStream(int32_t parameterIndex,const std::istringstream& value);
  void setNClob(int32_t parameterIndex,const NClob& value);
  void setNClob(int32_t parameterIndex,const std::istringstream& reader,const int64_t length);
  void setNClob(int32_t parameterIndex,const std::istringstream& reader);
  void setAsciiStream(int32_t parameterIndex, std::istream& stream, const int64_t length);
  void setAsciiStream(int32_t parameterIndex, std::istream& stream);
  void setAsciiStream(int32_t parameterIndex, std::istream& stream, int32_t length);
  void setBinaryStream(int32_t parameterIndex, std::istream& stream, const int64_t length);
  void setBinaryStream(int32_t parameterIndex, std::istream& stream);
  void setBinaryStream(int32_t parameterIndex, std::istream& stream,int32_t length);
  void setRowId(int32_t parameterIndex, const RowId* rowid);
#endif
#ifdef JDBC_SPECIFIC_TYPES_IMPLEMENTED
  void setBigDecimal(int32_t parameterIndex, const BigDecimal& bigDecimal);
  void setRef(int32_t parameterIndex, const Ref& ref);
  void setArray(int32_t parameterIndex,const sql::Array& array);
  void setDate(int32_t parameterIndex,const Date& date,const Calendar* cal);
  void setTimestamp(int32_t parameterIndex,const Timestamp& timestamp,const Calendar* cal);
  void setTime(int32_t parameterIndex,const Time& time, const Calendar* cal);
  void setDate(int32_t parameterIndex, const Date& date);
  void setTime(int32_t parameterIndex,const Time& time);
  void setTimestamp(int32_t parameterIndex,const Timestamp& timestamp);
  void setURL(int32_t parameterIndex,const URL& url);


  void setSQLXML(int32_t parameterIndex,const SQLXML& xmlObject);
  void setObject( int32_t parameterIndex,const sql::Object* obj,int32_t targetSqlType,int32_t scaleOrLength);
  void setObject(int32_t parameterIndex,const sql::Object* obj,int32_t targetSqlType);
  void setObject(int32_t parameterIndex,const sql::Object* obj);
  void setObject(int32_t parameterIndex,sql::Object* obj,SQLType* targetSqlType,int32_t scaleOrLength);
  void setObject(int32_t parameterIndex,sql::Object* obj,SQLType* targetSqlType);

private:
  /* Used only for setObject, which it's not cleare if we want and can support*/
  void setInternalObject( int32_t parameterIndex,const sql::Object* obj,int32_t targetSqlType,const int64_t scaleOrLength);
#endif

public:
  void setNull(int32_t parameterIndex, int32_t sqlType);
  void setNull(int32_t parameterIndex, const ColumnType& mariadbType);
  void setNull(int32_t parameterIndex, int32_t sqlType, const SQLString& typeName);
  void setBlob(int32_t parameterIndex, std::istream* inputStream, const int64_t length);
  void setBlob(int32_t parameterIndex, std::istream* inputStream);

  void setBoolean(int32_t parameterIndex,bool value);
  void setByte(int32_t parameterIndex, int8_t byte);
  void setShort(int32_t parameterIndex, int16_t value);
  void setString(int32_t parameterIndex, const SQLString& str);
  void setBytes(int32_t parameterIndex, sql::bytes* bytes);
  void setInt(int32_t column, int32_t value);
  void setLong(int32_t parameterIndex, int64_t value);
  void setInt64(int32_t parameterIndex, int64_t value) { setLong(parameterIndex, value); }
  void setUInt64(int32_t parameterIndex, uint64_t value);
  void setUInt(int32_t parameterIndex, uint32_t value);
  void setFloat(int32_t parameterIndex, float value);
  void setDouble(int32_t parameterIndex, double value);
  void setDateTime(int32_t parameterIndex, const SQLString& dt);
  void setBigInt(int32_t column, const SQLString& value);

  int32_t executeUpdate();
  int64_t executeLargeUpdate();
  bool execute();
  ResultSet* executeQuery();

  MariaDBExceptionThrower executeExceptionEpilogue(SQLException& sqle);

  /*** Inherited methods that should not work with Prepared/CallableStatement ***/
  void addBatch(const SQLString& sql);

  int32_t executeUpdate(const SQLString& sql);
  int32_t executeUpdate(const SQLString& sql, int32_t autoGeneratedKeys);
  int32_t executeUpdate(const SQLString& sql, int32_t* columnIndexes);
  int32_t executeUpdate(const SQLString& sql, const SQLString* columnNames);

  int64_t executeLargeUpdate(const SQLString& sql);
  int64_t executeLargeUpdate(const SQLString& sql, int32_t autoGeneratedKeys);
  int64_t executeLargeUpdate(const SQLString& sql, int32_t* columnIndexes);
  int64_t executeLargeUpdate(const SQLString& sql, const SQLString* columnNames);
  
  bool execute(const SQLString& sql);
  bool execute(const SQLString& sql, int32_t autoGeneratedKeys);
  bool execute(const SQLString& sql, int32_t* columnIndexes);
  bool execute(const SQLString& sql, const SQLString* columnNames);

  ResultSet* executeQuery(const SQLString& sql);

  /* Forwarding to stmt to implement Statement's part of interface */
  uint32_t getMaxFieldSize()         { return stmt->getMaxFieldSize(); }
  void setMaxFieldSize(uint32_t max) { stmt->setMaxFieldSize(max); }
  int32_t getMaxRows()              { return stmt->getMaxRows(); }
  void setMaxRows(int32_t max)      { stmt->setMaxRows(max); }
  int64_t getLargeMaxRows()         { return stmt->getLargeMaxRows(); }
  void setLargeMaxRows(int64_t max) { stmt->setLargeMaxRows(max); }
  void setEscapeProcessing(bool enable) { stmt->setEscapeProcessing(enable); }
  int32_t getQueryTimeout()             { return stmt->getQueryTimeout(); }
  void setQueryTimeout(int32_t seconds) { stmt->setQueryTimeout(seconds); }
  void cancel()             { stmt->cancel(); }
  SQLWarning* getWarnings() { return stmt->getWarnings(); }
  void clearWarnings()      { stmt->clearWarnings(); }
  void setCursorName(const SQLString& name) { stmt->setCursorName(name); }
  Connection* getConnection()               { return stmt->getConnection(); }
  ResultSet* getGeneratedKeys()             { return stmt->getGeneratedKeys(); }
  int32_t getResultSetHoldability()         { return stmt->getResultSetHoldability(); }
  bool isClosed()                 { return stmt->isClosed(); }
  bool isPoolable()               { return stmt->isPoolable(); }
  void setPoolable(bool poolable) { stmt->setPoolable(poolable); }
  ResultSet* getResultSet()       { return stmt->getResultSet(); }
  int32_t getUpdateCount()        { return stmt->getUpdateCount(); }
  int64_t getLargeUpdateCount()   { return stmt->getLargeUpdateCount(); }
  bool getMoreResults()           { return stmt->getMoreResults(); }
  bool getMoreResults(int32_t current) { return stmt->getMoreResults(current); }
  int32_t getFetchDirection()          { return stmt->getFetchDirection(); }
  void setFetchDirection(int32_t direction) { stmt->setFetchDirection(direction); }
  int32_t getFetchSize()            { return stmt->getFetchSize(); }
  void setFetchSize(int32_t rows)   { stmt->setFetchSize(rows); }
  int32_t getResultSetConcurrency() { return stmt->getResultSetConcurrency(); }
  int32_t getResultSetType()        { return stmt->getResultSetType(); }
  void closeOnCompletion()    { stmt->closeOnCompletion(); }
  bool isCloseOnCompletion()  { return stmt->isCloseOnCompletion(); }
  Statement* setResultSetType(int32_t rsType) { stmt->setResultSetType(rsType); return this; }

  };
}
}
#endif
